#include <common.h>
#include <init.h>
#include <spl.h>
#include <asm/addrspace.h>
#include <asm/sections.h>
#include <asm/io.h>
#include <asm/global_data.h>
#include <spi.h>
#include <spi_flash.h>
#include <mach/loongson.h>
#include <image.h>

DECLARE_GLOBAL_DATA_PTR;

void board_boot_order(u32 *spl_boot_list)
{
	int n = 0;
#ifdef CONFIG_SPI_FLASH_BAR
	spl_boot_list[n++] = BOOT_DEVICE_BOOTSPACE;
#endif

#ifdef CONFIG_SPL_SPI_FLASH_SUPPORT
	spl_boot_list[n++] = BOOT_DEVICE_SPI;
#endif
#ifdef CONFIG_SPL_MMC
	spl_boot_list[n++] = BOOT_DEVICE_MMC1;
#endif
#ifdef CONFIG_SPL_NAND_SUPPORT
	spl_boot_list[n++] = BOOT_DEVICE_NAND;
#endif
}

static unsigned int spl_get_uboot_offs(void)
{
	ulong spl_size, fdt_size;
	unsigned int offs;

	fdt_size = fdt_totalsize(gd->fdt_blob);
	spl_size = (ulong)&_image_binary_end - (ulong)__text_start;

	offs = (unsigned int)(spl_size + fdt_size);
	return offs;
}

#ifdef CONFIG_SPL_SPI_LOAD
unsigned int spl_spi_get_uboot_offs(struct spi_flash *flash)
{
    return spl_get_uboot_offs();
}
#endif

void spl_board_prepare_for_boot(void)
{
    unsigned long unlock_base = LOCK_CACHE_BASE;

    // unlock scache
	// the scache locked in lowlevel_init.S is used by stack for early stage.
	// now our stack sp is in sdram, so unlock the scache.
	writeq(0x0, LS_SCACHE_LOCK_WIN0_BASE);
	writeq(0x0, LS_SCACHE_LOCK_WIN0_MASK);
	
	/* flush scache using hit-invalidate */
	for (int i = 0; i < LOCK_CACHE_SIZE; i += 0x40) {
		asm(
			"cacop 0x13, %0, 0\n\t"
			:
			: "r"(unlock_base)
		);
		unlock_base += 0x40;
	}
}

#ifdef CONFIG_SPL_DISPLAY_PRINT
void spl_display_print(void)
{
	printf("\n"
		"  _    ___   ___  _  _  ___ ___  ___  _  _\n"
		" | |  / _ \\ / _ \\| \\| |/ __/ __|/ _ \\| \\| |\n"
		" | |_| (_) | (_) | .` | (_ \\__ \\ (_) | .` |\n"
		" |____\\___/ \\___/|_|\\_|\\___|___/\\___/|_|\\_|\n"
		"\n");
}
#endif

#ifdef CONFIG_SPI_FLASH_BAR
static int spl_bootspace_load_image(struct spl_image_info *spl_image,
			      struct spl_boot_device *bootdev)
{
	struct image_header *header;
	unsigned int payload_offs;

	u8 *boot_device_base = map_sysmem(BOOT_SPACE_BASE, 0);
	int err;

	payload_offs = spl_get_uboot_offs();

	header = spl_get_load_buffer(-sizeof(*header), sizeof(*header));

	memcpy((void *)header, boot_device_base + payload_offs, sizeof(*header));

	err = spl_parse_image_header(spl_image, header);
	if (err)
		return err;
	
	memcpy((void *)spl_image->load_addr,
			boot_device_base + payload_offs, 
			spl_image->size);

	return 0;
}

SPL_LOAD_IMAGE_METHOD("BootSpace", 1, BOOT_DEVICE_BOOTSPACE, spl_bootspace_load_image);
#endif
